package com.api.tournament.service.impl;

import com.api.core.ServiceException;
import com.api.tournament.dao.*;
import com.api.tournament.model.*;
import com.api.tournament.service.StageService;
import com.api.core.service.AbstractService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.api.common.JSONUtils;
import com.api.core.response.Result;
import com.api.core.response.ResultGenerator;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.util.CollectionUtils;

import static com.api.tournament.contonst.Const.*;

/**
 * Created by wanghuiwen on 2021/03/23.
 */
@Service
@Transactional
public class StageServiceImpl extends AbstractService<Stage> implements StageService {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private StageMapper stageMapper;
    @Resource
    private StageRankMapper stageRankMapper;
    @Resource
    private RoundMapper roundMapper;
    @Resource
    private RoundLogMapper roundLogMapper;
    @Resource
    private TournamentTeamMapper tournamentTeamMapper;
    @Resource
    private TournamentMapper tournamentMapper;

    @Override
    public Result list(String search, String order, Integer page, Integer size){
        Map<String, Object> params = JSONUtils.json2map(search);
        Map<String, Object> orderParams = JSONUtils.json2map(order);
        for (String key : orderParams.keySet()) {
                if (orderParams.get(key) != null && orderParams.get(key).equals("ascending")) orderParams.put(key, "asc");
                if (orderParams.get(key) != null && orderParams.get(key).equals("descending")) orderParams.put(key, "desc");
            }
        PageHelper.startPage(page, size);
        List<Map<String, Object>> res = stageMapper.baseList(params, orderParams);
        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>(res);
        return ResultGenerator.genSuccessResult(pageInfo);
    }

    @Override
    public Result start(Long stageId) {
        Stage stage = findById(stageId);
        stage.setEnd(false);
        update(stage);
        return ResultGenerator.genSuccessResult();
    }

    @Override
    public Result stop(Long stageId) {
        List<RoundLog>  noRoundLogs = roundLogMapper.selectByStageNoResult(stageId);
        if(!CollectionUtils.isEmpty(noRoundLogs)) {
            return ResultGenerator.genFailResult(400,"There is some match not yet complete");
        }
        Stage stage = findById(stageId);
        stage.setEnd(true);
        update(stage);

        List<Stage> stages = stageMapper.selectByTournamentId(stage.getTournamentId());

        Stage  nextStage = null;
        //判断是否是最后一个阶段
        for (int i = 0; i < stages.size(); i++) {

            if(stageId.equals(stages.get(i).getId()) && i<stages.size()-1){
                nextStage =stages.get(i+1);
                break;
            }
        }
        // 如果不是最后一个阶段 确定下阶段的参与人
        if(nextStage!=null){
            List<String> groups = stageRankMapper.groupName(stage.getId());
            //下阶段参赛的人数
            List<StageRank> nextRank = new ArrayList<>();
            //淘汰的队伍
            List<TournamentTeam> tournamentTeams = new ArrayList<>();
            //取每组前getPromotionSize 位
            for (String groupName : groups) {
                List<StageRank> stageRanks =  stageRankMapper.promotion(null,stage.getId(),groupName);
                //晋级的人
                for (int i = 0; i < stage.getPromotionSize(); i++) {
                    stageRanks.get(i).setPromotion(true);
                    stageRankMapper.updateByPrimaryKey(stageRanks.get(i));
                    //晋级的队伍新建
                    StageRank nextStageRank = new StageRank();
                    nextStageRank.setTournamentUserId(stageRanks.get(i).getTournamentUserId());
                    nextStageRank.setPromotion(false);
                    nextStageRank.setStageId(nextStage.getId());
                    nextRank.add(nextStageRank);
                }
                //淘汰的人确定排名
                for (int i = stage.getPromotionSize(); i < stageRanks.size(); i++) {
                    TournamentTeam tournamentTeam = new TournamentTeam();
                    tournamentTeam.setTournamentUserId(stageRanks.get(i).getTournamentUserId());
                    tournamentTeam.setWin(stageRanks.get(i).getWin());
                    tournamentTeam.setLose(stageRanks.get(i).getLose());
                    tournamentTeam.setDraw(stageRanks.get(i).getDraw());
                    tournamentTeams.add(tournamentTeam);
                }
            }

            tournamentTeams =  tournamentTeams.stream().sorted(Comparator.comparing(TournamentTeam::getWin)).collect(Collectors.toList());
            // 确定淘汰队伍的大排名
            Integer maxTeam = tournamentTeamMapper.maxTeam(stage.getTournamentId());
            for (int i = 0; i < tournamentTeams.size(); i++) {
                tournamentTeams.get(i).setRank(maxTeam - i);
                tournamentTeamMapper.updateByPrimaryKeySelective(tournamentTeams.get(i));
            }

            for (int i = 0; i < nextRank.size(); i++) {
                nextRank.get(i).setGroubName(GROUP_NAME[i%(nextStage.getGroupSize()==0 ?1 : nextStage.getGroupSize())]);

            }

            //删除round log
            roundLogMapper.deleteByByStageId(nextStage.getId());
            //删除round
            roundMapper.deleteByByStageId(nextStage.getId());
            stageRankMapper.deleteByStageId(nextStage.getId());
            stageRankMapper.insertList(nextRank);

            /**
             * 生成下阶段对战信息
             */
            /**
             * 单败模式
             */
            if(nextStage.getFormat()==GAME_FORMAT_SINGLE) {
                int tournamentUserSize = nextRank.size();
                //参赛人数必须是2的整数次幂
                double log = Math.log(tournamentUserSize)/Math.log(2);

                /**
                 * 确定round数量
                 */
                List<Round> rounds = new ArrayList<>();
                for (int i = 0; i < log ; i++) {
                    Round round = new Round();
                    round.setStageId(nextStage.getId());
                    round.setRoundName(String.valueOf(i+1));
                    round.setGourpName("A");
                    roundMapper.insertSelective(round);
                    rounds.add(round);
                }
                //第一轮的参赛队伍
                List<RoundLog> roundLogs = new ArrayList<>();

                //如果不够计算 第一轮的参赛队伍数量 和直接晋级下一轮的队伍
                if(Math.round(log) != log){
                    //应该有多少队伍才够
                    tournamentUserSize = (int) Math.pow(2,Math.ceil(log));

                    int luckySize = tournamentUserSize - nextRank.size();
                    //直接晋级的队伍
                    for ( int i = 0; i <luckySize ; i+=2) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        roundLog.setTournamentUserIdA(nextRank.get(i).getTournamentUserId());
                        if(luckySize > i+1){
                            roundLog.setTournamentUserIdB(nextRank.get(i+1).getTournamentUserId());
                            roundLog.setLuckyBId(nextRank.get(i+1).getTournamentUserId());

                        }
                        roundLog.setLuckyAId(nextRank.get(i).getTournamentUserId());
                        //第二个round
                        roundLog.setRoundId(rounds.get(1).getRoundId());
                        roundLog.setType(1);
                        roundLogs.add(roundLog);
                    }
                    //空队伍放前面
                    Collections.reverse(roundLogs);
                    //第一轮参赛的队伍
                    tournamentUserSize =  nextRank.size() - luckySize;
                    for ( int i = 0; i <tournamentUserSize ; i+=2) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        roundLog.setTournamentUserIdA(nextRank.get(i+luckySize).getTournamentUserId());
                        roundLog.setTournamentUserIdB(nextRank.get(i+luckySize+1).getTournamentUserId());
                        //第一的round
                        roundLog.setRoundId(rounds.get(0).getRoundId());
                        roundLog.setType(1);
                        roundLogs.add(roundLog);
                    }
                    if(rounds.size()>2){
                        genRoundLog(roundLogs,(int) Math.pow(2,Math.ceil(log)),2,nextStage.getId(),rounds,1);
                    }
                }
                else {

                    for (int i = 0; i <nextRank.size() ; i+=2) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        roundLog.setTournamentUserIdA(nextRank.get(i).getTournamentUserId());
                        roundLog.setTournamentUserIdB(nextRank.get(i+1).getTournamentUserId());
                        roundLog.setRoundId(rounds.get(0).getRoundId());
                        roundLog.setType(1);
                        roundLogs.add(roundLog);
                    }
                    if(rounds.size()>1){
                        genRoundLog(roundLogs,(int) Math.pow(2,Math.ceil(log)),2, nextStage.getId(), rounds,1);
                    }
                }

                List<StageRank> stageRanks = new ArrayList<>();
                roundLogMapper.insertList(roundLogs);
                //生成 stage_rank 信息
                for (StageRank tournamentUser : nextRank) {
                    StageRank rank = new StageRank();
                    rank.setTournamentUserId(tournamentUser.getTournamentUserId());
                    rank.setStageId(nextStage.getId());
                    rank.setPromotion(false);
                    rank.setGroubName("A");
                    stageRanks.add(rank);
                }
                stageRankMapper.insertList(stageRanks);
            }

            /**
             * 双败制
             */
            if(nextStage.getFormat() == GAME_FORMAT_DOUBLE){


                int tournamentUserSize = nextRank.size();
                //参赛人数必须是2的整数次幂
                double log = Math.log(tournamentUserSize)/Math.log(2);

                /**
                 * 确定胜者组round数量
                 */
                List<Round> rounds = new ArrayList<>();
                for (int i = 0; i < log ; i++) {
                    Round round = new Round();
                    round.setStageId(nextStage.getId());
                    round.setRoundName(String.valueOf(i));
                    round.setGourpName("A");
                    roundMapper.insertSelective(round);
                    rounds.add(round);
                }

                /**
                 *
                 *胜者组round的数量*2-2-ROUND(胜者组round的数量-ROUNDDOWN(LOG(参数人数,2),1),0)
                 */

                int loserRoundsSize= 3*2-2-(int) Math.round(3- BigDecimal.valueOf(log).setScale(1,BigDecimal.ROUND_HALF_UP).doubleValue());

                /**
                 * 胜者组
                 */
                //第一轮的参赛队伍
                List<RoundLog> roundLogs = new ArrayList<>();
                //如果不够计算第一轮的参赛队伍数量 和直接晋级下一轮的队伍
                if(Math.round(log) != log){
                    tournamentUserSize = (int) Math.pow(2,Math.ceil(log));

                    int luckySize = tournamentUserSize - nextRank.size();
                    //第二回合的对阵数量
                    int luckySizeRound = tournamentUserSize/4;
                    for (int i = 0; i <luckySizeRound ; i++) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        if(luckySizeRound +i < luckySize ){
                            roundLog.setTournamentUserIdB(nextRank.get(luckySizeRound +i).getTournamentUserId());
                            roundLog.setLuckyBId(nextRank.get(luckySizeRound +i).getTournamentUserId());
                        }
                        if(i<luckySize){
                            roundLog.setTournamentUserIdA(nextRank.get(i).getTournamentUserId());
                            roundLog.setLuckyAId(nextRank.get(i).getTournamentUserId());
                        }
                        //第二的round
                        roundLog.setRoundId(rounds.get(1).getRoundId());
                        rounds.get(1).getLogs().add(roundLog);
                        roundLog.setType(1);
                        roundLogs.add(roundLog);
                    }
                    Collections.reverse(roundLogs);

                    //第一轮参赛的队伍
                    tournamentUserSize =  nextRank.size() - luckySize;
                    for (int i = 0; i <tournamentUserSize ; i+=2) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        roundLog.setTournamentUserIdA(nextRank.get(i+luckySize).getTournamentUserId());
                        roundLog.setTournamentUserIdB(nextRank.get(i+luckySize+1).getTournamentUserId());
                        //第一的round
                        roundLog.setRoundId(rounds.get(0).getRoundId());
                        rounds.get(0).getLogs().add(roundLog);
                        roundLog.setType(1);
                        roundLogs.add(roundLog);
                    }
                    if(rounds.size()>2){
                        genRoundLog(roundLogs,(int) Math.pow(2,Math.ceil(log)),3,nextStage.getId(),rounds,1);
                    }
                }
                else {
                    for (int i = 0; i <nextRank.size() ; i+=2) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        roundLog.setTournamentUserIdA(nextRank.get(i).getTournamentUserId());
                        roundLog.setTournamentUserIdB(nextRank.get(i+1).getTournamentUserId());
                        roundLog.setRoundId(rounds.get(0).getRoundId());
                        rounds.get(0).getLogs().add(roundLog);
                        roundLog.setType(1);
                        roundLogs.add(roundLog);
                    }
                    if(rounds.size()>1){
                        genRoundLog(roundLogs,(int) Math.pow(2,Math.ceil(log)),2, nextStage.getId(), rounds,1);
                    }
                }

                /**
                 * 如果是双败制 胜者组需要增加一个回合
                 */
                Round round = new Round();
                round.setStageId(nextStage.getId());
                round.setRoundName(String.valueOf(rounds.size()));
                round.setGourpName("A");
                roundMapper.insertSelective(round);
                rounds.add(round);

                RoundLog lastRoundLog = new RoundLog();
                lastRoundLog.setStageId(nextStage.getId());
                lastRoundLog.setRoundId(rounds.get(rounds.size()-1).getRoundId());
                rounds.get(rounds.size()-1).getLogs().add(lastRoundLog);
                lastRoundLog.setType(1);
                roundLogs.add(lastRoundLog);
                /**
                 * 败者组
                 */
                /**
                 * 败者组round
                 */
                List<Round> loserRounds =  new ArrayList<>();
                for (int i = 0; i < loserRoundsSize ; i++) {
                    Round loserRound = new Round();
                    loserRound.setStageId(nextStage.getId());
                    loserRound.setRoundName(String.valueOf(i));
                    loserRound.setGourpName("A");
                    loserRound.setRoundType(2);
                    roundMapper.insertSelective(loserRound);
                    loserRounds.add(loserRound);
                }

                /**
                 * 败者组参赛的队伍
                 */
                int size = rounds.get(0).getLogs().size() + rounds.get(1).getLogs().size();
                double loserLog = Math.log(size)/Math.log(2);
                //理想的参赛人数
                int loserSize = (int) Math.pow(2,Math.ceil(loserLog));

                //第一回合的对阵数量
                int firstLogSize = (size - (loserSize - size)) /2;

                //第二回合的对阵数量
                int secRound = loserSize/4;

                /**
                 * 不是刚好2的次方
                 */
                if(Math.round(loserLog) != loserLog){
                    /**
                     * 败者组第一个round的对阵
                     */
                    for (int i1 = 0; i1 < firstLogSize; i1++) {
                        RoundLog roundLog = new RoundLog();
                        roundLog.setStageId(nextStage.getId());
                        roundLog.setRoundId(loserRounds.get(0).getRoundId());
                        roundLog.setGroupName("A");
                        roundLog.setType(2);
                        loserRounds.get(0).getLogs().add(roundLog);
                        roundLogs.add(roundLog);
                    }

                    for (int i = 1; i < loserRounds.size(); i++) {
                        //这个败者round 要有几次对阵
                        logger.warn(String.valueOf(secRound));

                        /**
                         * 如果败者组对阵是偶数 则每个偶数对阵 对阵数量减少一半  如果是奇数则每个奇数round对阵数量减少一半
                         */
                        if(loserRounds.size()%2==0){
                            if(i%2 == 0){
                                secRound  = secRound/2;
                            }
                        }else {
                            if(i%2 == 1){
                                secRound  = secRound/2;
                            }
                        }
                        for (int i1 = 0; i1 < secRound; i1++) {
                            RoundLog roundLog = new RoundLog();
                            roundLog.setStageId(nextStage.getId());
                            roundLog.setRoundId(loserRounds.get(i).getRoundId());
                            roundLog.setGroupName("A");
                            roundLog.setType(2);
                            //有连两条线的情况
                            if(loserRounds.get(i-1).getLogs().size() > secRound){

                                if(i1<loserRounds.get(i-1).getLogs().size() - secRound){
                                    roundLog.setLuckyAId((long) i1*2);
                                    roundLog.setLuckyBId((long) i1*2+1);
                                }else {
                                    roundLog.setLuckyAId((long) i1+loserRounds.get(i-1).getLogs().size() - secRound);
                                }
                            }else {
                                if(loserRounds.get(i-1).getLogs().size()>i1){
                                    roundLog.setLuckyAId((long) i1);
                                }
                            }
                            loserRounds.get(i).getLogs().add(roundLog);
                            roundLogs.add(roundLog);
                        }
                    }

                }
                else {
                    for (int i = 0; i < loserRounds.size(); i++) {
                        //这个败者round 要有几次对阵
                        logger.warn(String.valueOf(firstLogSize));
                        if(i%2 == 1){
                            firstLogSize  = firstLogSize/2;
                        }
                        for (int i1 = 0; i1 < firstLogSize; i1++) {
                            RoundLog roundLog = new RoundLog();
                            roundLog.setStageId(nextStage.getId());
                            roundLog.setRoundId(loserRounds.get(i).getRoundId());
                            roundLog.setGroupName("A");
                            if(i%2 == 1){
                                roundLog.setLuckyAId((long) i1*2);
                                roundLog.setLuckyBId((long) i1*2+1);
                            }
                            if(i%2 == 0 && i!=0) {
                                roundLog.setLuckyAId((long) i1);
                            }
                            roundLog.setType(2);
                            loserRounds.get(i).getLogs().add(roundLog);
                            roundLogs.add(roundLog);
                        }
                    }

                }
                //生成 stage_rank 信息
                List<StageRank> stageRanks = new ArrayList<>();
                for (StageRank tournamentUser : nextRank) {
                    StageRank rank = new StageRank();
                    rank.setTournamentUserId(tournamentUser.getTournamentUserId());
                    rank.setStageId(nextStage.getId());
                    rank.setGroubName("A");
                    rank.setPromotion(false);
                    stageRanks.add(rank);
                }
                stageRankMapper.insertList(stageRanks);
                roundLogMapper.insertList(roundLogs);


            }
        }
        else {
            Tournament tournament = tournamentMapper.selectByPrimaryKey(stage.getTournamentId());
            tournament.setStatus(TOURNAMENT_STATUS_END);
            tournamentMapper.updateByPrimaryKeySelective(tournament);
        }

        return ResultGenerator.genSuccessResult();
    }

    /**
     * 生成空的对战信息
     * @param roundLogs
     * @param pow 参数总对数 2的次方
     * @param i 从地几轮开车生成
     * @param id
     * @param rounds
     */
    private void genRoundLog(List<RoundLog> roundLogs, int pow, int i, Long id, List<Round> rounds,int type) {
        Map<Long, List<RoundLog>> groupBySex = roundLogs.stream().collect(Collectors.groupingBy(RoundLog::getRoundId));
        //本轮对战次数
        for (int k = i-1; k < rounds.size() ; k++) {
            List<RoundLog> logs = groupBySex.get(rounds.get(k).getRoundId());
            int size = (int) (pow / (Math.pow(2, k + 1)));
            if (logs != null) {
                size = size - logs.size();
                ; //2 4 8
            }
            for (int j = 0; j < size; j++) {
                RoundLog roundLog = new RoundLog();
                roundLog.setStageId(id);
                roundLog.setRoundId(rounds.get(k).getRoundId());
                roundLog.setType(type);
                roundLogs.add(roundLog);
                rounds.get(k).getLogs().add(roundLog);
            }
        }
    }
}
